﻿/*

filipesilvestrim - An open-source ActionScript 3.0 repository of codes for Game Developers 

http://code.google.com/p/filipesilvestrim/

Copyright (c) 2009 Filipe Ghesla Silvestrim, All Rights Reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

*/

package com.filipesilvestrim.game.core
{
	import com.filipesilvestrim.manager.AbstractManager;
	
	import flash.display.DisplayObjectContainer;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.TimerEvent;
	import flash.text.TextField;
	import flash.utils.Timer;
	import flash.utils.getTimer;

	public class Updater extends AbstractManager
	{
		// ___________________________________________________________________ CONSTANTS
		public const TICKS_PER_SECOND 	: int = 25; //Delay
    	public const SKIP_TICKS 		: int = 1000 / TICKS_PER_SECOND;
    	public const MAX_FRAMESKIP 		: int = 5;
		
		// ___________________________________________________________________ CLASS PROPERTIES
   		private var _loops				: int;
    	private var _interpolation		: Number;
    	private var _accruedTime		: Number	= 0;
	    private var _lastGameTick		: int;
	    private var _nextGameTick		: int 		= getTimer();
	    private var _isRunning 			: Boolean 	= true;
		
		// ___________________________________________________________________ INSTANCE PROPERTIES
		private var _shape				: Shape;
		private var _mainStage			: Stage;
		private var _mainClock			: Timer;
		
		//-- fps
		private var _fpsText			: TextField;
		private var _fpsFr				: int		= 0;
		private var _fpsMs				: int		= getTimer();
		private var _fpsIsAble			: Boolean	= false;
		
		// ___________________________________________________________________ GETTERS AND SETTERS
		public function get deltaTime()							: Number	
		{ 
			var currentTime : Number 	= getTimer();
			var deltaTime 	: Number 	= currentTime - _lastGameTick;
			_lastGameTick 				= currentTime;
			return deltaTime; 
		}
		
		public function get isRunning()							: Boolean 	{ return _isRunning; }
		public function set maximuFrameRate( value : Number )	: void 		{ _mainStage.frameRate = value; }
	
		// ___________________________________________________________________ CONSTRUCTOR
		public function Updater ( mainStage : Sprite ) 
		{
			_mainStage		= mainStage.stage;
        	_loops 			= 0;
			_shape			= new Shape();
			_mainClock		= new Timer(10);
			
			maximuFrameRate	= 33.33333;
		}
		
		// ___________________________________________________________________ PUBLIC METHODS
		public function start() : void
		{
			_isRunning 				= true;
			_lastGameTick 			= getTimer();
			
			_shape.addEventListener( Event.ENTER_FRAME, mainLoop, false, 0, true );
//			_mainClock.addEventListener( TimerEvent.TIMER, mainLoop, false, 0, true );
//			_mainStage.addEventListener( Event.RENDER, mainLoop, false, 0, true);
//			_mainClock.start();
		}

		public function stop() : void
		{
			_mainClock.stop();
			_shape.removeEventListener( Event.ENTER_FRAME, mainLoop );
//			_mainClock.removeEventListener( TimerEvent.TIMER, mainLoop );
//			_mainStage.removeEventListener( Event.RENDER, mainLoop);
			
			_isRunning = false;
		}
		
		public function addFPS ( where : DisplayObjectContainer ) : void
		{
			_fpsIsAble			= true;
			_fpsText 			= new TextField();
			_fpsText.autoSize 	= 'left';
			
			where.addChild( _fpsText );
		}
		
		// ___________________________________________________________________ PRIVATE METHODS
		private function mainLoop ( event : Event ) : void
		{
			_loops = 0;
			
//			_accruedTime += deltaTime;
//			if( _accruedTime >= TICKS_PER_SECOND )
//			{
//				_accruedTime -= TICKS_PER_SECOND;
//	
//			}
//		
//			if ( getTimer() > _nextGameTick && _loops < MAX_FRAMESKIP) 
//			{
				update();
				          
	            //force visual update
//		        _mainStage.invalidate();
	
//	            _nextGameTick 	+= SKIP_TICKS;
//	            _loops++;
//	        }
	
//	        _interpolation = ( getTimer() + SKIP_TICKS - _nextGameTick ) / SKIP_TICKS;
			display();
				        
	        //updatevisual
//	        if (event is TimerEvent) { (event as TimerEvent).updateAfterEvent(); }
	        
	        /**
	        * fps
	        *
			_fpsFr++;
			if( _fpsMs + 1000 < getTimer())
			{
				if(_fpsIsAble) { _fpsText.text 	= _fpsFr.toString(); }
				_fpsFr 			= 0;
				_fpsMs 			= getTimer();
			}
			 * */
		}
		
		/**
		 * Game Speed
		 * Game Speed is the number of times the game state gets updated per second, or in other words, the number of times update_game() is called per second.
		 */ 
		private function update () : void
		{
			for each ( var node : * in data)
			{
				if (node != null)
					node.update();
			}
		}
		
		/**
		 * FPS
		 * FPS is an abbreviation for Frames Per Second. In the context of the above implementation, it is the number of times display_game() is called per second.
		 * @param interpolation - So basically the interpolation variable contains the value that is in between the previous gametick and the next one (previous = 0.0, next = 1.0). What you have to do then is make a "prediction" function where the car/camera/... would be placed at the render time. You can base this prediction function on the speed of the object, steering or rotation speed. It doesn't need to be complicated because we only use it to smooth things out in between the frames. It is indeed possible that an object gets rendered into another object right before a collision gets detected. But like we have seen before, the game is updated 25 frames per second, and so when this happens, the error is only shown for a fraction of a second, hardly noticeable to the human eye.
		 */
		private function display () : void
		{
			for each ( var node : * in data)
			{
				if (node != null)
					node.draw(_interpolation);
			}
		}
		
		/**
		private function move() : void
		{
		    var deltaTime : int = m_deltaTimer.calculateDeltaTime();
		
		    var speed : Number = 360; // pixels per second
		    var speedInMilliseconds : Number = speed / 1000; // pixels per millisecond
		    var distanceTraveled : Number = deltaTime * speedInMilliseconds; // distance = speed * time
		
		    m_gryphon.x += distanceTraveled;
		}
		 * 
		 If in the 10Th gametick the position is 500, and the speed is 100, then in the 11Th gametick the position will be 600. So where will you place your car when you render it? You could just take the position of the last gametick (in this case 500). But a better way is to predict where the car would be at exact 10.3, and this happens like this:
  		 view_position = position + (speed * interpolation)
		
		*/

		// ___________________________________________________________________ EVENTS
	}
}

